/**
 * @File name: stm32f4_stdio.c
 * @Synopsis:
 * @Author: xing.dai@aliyun.com
 * @Version: 0.0.1
 * @Date: 2018-08-04 08:40
 */

#include <stdio.h>
#include <stdlib.h>
#include "stm32f4xx_conf.h"
#include "types.h"

#ifdef __GNUC__		/* GNU Compipler*/
#if 0
int __io_putchar(int ch)
{
    /* lace your implementation of fputc here */
    /* e.g. write a character to the USART */
    if (ch == '\n') {
        while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) != SET);
        USART_SendData(USART1, (uint8_t)'\r');
    }

    USART_SendData(USART1, (uint8_t) ch);
    /* Loop until the end of transmission */
    while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) != SET) {
    }

    return ch;

}

#else

void get_buffered_line(void);

#define BUFLEN	127

static uint16_t start_ndx;
static uint16_t end_ndx;
static char buf[BUFLEN + 1];
#define buf_len ((end_ndx - start_ndx) % BUFLEN)

static inline int inc_ndx(int n)
{
    return ((n + 1) % BUFLEN);
}

static inline int dec_ndx(int n)
{
    return ((n + BUFLEN - 1) % BUFLEN);
}

static inline void back_up(void)
{

}

void get_buffered_line(void)
{
    char c;

    if (start_ndx != end_ndx)
        return;

    while (1) {
        while (USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == RESET) {
        }
        c = USART_ReceiveData(USART1);

        switch (c) {
            case '\r':
                buf[end_ndx] = '\n';
                end_ndx = inc_ndx(end_ndx);
                buf[end_ndx] = '\0';
                return;
            default:
                if (buf_len == (BUFLEN - 1)) {
                } else {
                    buf[end_ndx] = c;
                    end_ndx = inc_ndx(end_ndx);
                }
                break;
        }
    }
}

int _read(int fd, char *ptr, int len)
{
    int my_len;

    if (fd > 2)
        return -1;

    get_buffered_line();
    my_len = 0;

    while ((buf_len > 0) && (len > 0)) {
        *ptr++ = buf[start_ndx];
        start_ndx = inc_ndx(start_ndx);
        my_len++;
        len--;
    }

    return my_len;
}

/**
 * @Synopsis  Called by libc stdio fwrite functions
 *				Retargets the C library printf function to the usart.
 *
 * @Returns
 */
int _write(int fd, char *ptr, int size)
{
    int count = 0;

    if (fd > 2)
        return -1;	/* Only work for stdout, stdin, stderr. */

    while (*ptr && (count < size)) {
        if (*ptr == '\n') {
            while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET);
            USART_SendData(USART1, (uint8_t)'\r');
        }
        /* Loop until the end of transmission */
        while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET) {
        }
        /* Place your implementation of fputc here */
        /* e.g. write a character to the USART */
        USART_SendData(USART1, (uint8) *ptr);
        count++;
        ptr++;
    }
    return count;
}
#endif
#else	/* ARMCC Compipler. */

/**
  * @brief  Retargets the C library printf function to the USART.
  * @param  ch: the char to be send.
  * @param  *f:
  * @retval the char that send out.
  */
int fputc(int ch, FILE *f)
{
    /* lace your implementation of fputc here */
    /* e.g. write a character to the USART */
    if (ch == '\n') {
        while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET);
        USART_SendData(USART1, (uint8_t)'\r');
    }

    /* Loop until the end of transmission */
    while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET) {
    }
    USART_SendData(USART1, (uint8_t) ch);

    return ch;
}

/**
  * @brief  Retargets the C library printf function to the USART.
  * @param  *f
  * @retval the char that received.
  */
int fgetc(FILE *f)
{
    int ch;
    while (USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == RESET) {
    }
    ch = USART_ReceiveData(USART1);

    return ch;
}

#endif
